// EditorView.cpp : implementation of the CEditorView class
//

#include "stdafx.h"
#include "MapEditor.h"
#include "MemDC.h"
#include "EditorDoc.h"
#include "EditorView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define VIEW_ORG_X	16
#define VIEW_ORG_Y	16

/////////////////////////////////////////////////////////////////////////////
// CEditorView

IMPLEMENT_DYNCREATE(CEditorView, CScrollView)

BEGIN_MESSAGE_MAP(CEditorView, CScrollView)
	//{{AFX_MSG_MAP(CEditorView)
	ON_WM_ERASEBKGND()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_MOUSEWHEEL()
	ON_UPDATE_COMMAND_UI(ID_VIEW_GRID, OnUpdateViewGrid)
	ON_COMMAND(ID_VIEW_GRID, OnViewGrid)
	ON_WM_KEYDOWN()
	ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
	ON_UPDATE_COMMAND_UI(ID_ERASE, OnUpdateErase)
	ON_COMMAND(ID_ERASE, OnErase)
	ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
	ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
	ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
	ON_COMMAND(ID_CLEAR_ALL, OnClearAll)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CEditorView construction/destruction

CEditorView::CEditorView()
{
	m_bViewGrid = TRUE;
	m_bScreen = TRUE;
	m_bErase = FALSE;
	m_nLevel = 0;
}

CEditorView::~CEditorView()
{
}

BOOL CEditorView::PreCreateWindow(CREATESTRUCT& cs)
{
	return CScrollView::PreCreateWindow(cs);
}


void CEditorView::OnInitialUpdate()
{
	CScrollView::OnInitialUpdate();

	CSize sizeTotal;
	sizeTotal.cx = sizeTotal.cy = 300;

	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if(pDoc->m_pGrids)
	{
		int cx = pDoc->m_nCX;
		int cy = pDoc->m_nCY;
		int tx = pDoc->m_nTx;
		int ty = pDoc->m_nTy;

		sizeTotal.cx = VIEW_ORG_X + cx * tx + VIEW_ORG_X;
		sizeTotal.cy = VIEW_ORG_Y + cy * ty + VIEW_ORG_Y;
	}
	SetScrollSizes(MM_TEXT, sizeTotal);

	m_ptScreen.x = VIEW_ORG_X;
	m_ptScreen.y = VIEW_ORG_Y;

	VERIFY(m_brBkgnd.CreateSolidBrush(RGB(128, 128, 128)));
	VERIFY(m_pnLine.CreatePen(PS_SOLID, 1, RGB(191, 191, 191)));
	VERIFY(m_pnScreen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0)));
}

/////////////////////////////////////////////////////////////////////////////
// CEditorView diagnostics

#ifdef _DEBUG
void CEditorView::AssertValid() const
{
	CScrollView::AssertValid();
}

void CEditorView::Dump(CDumpContext& dc) const
{
	CScrollView::Dump(dc);
}

CEditorDoc* CEditorView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CEditorDoc)));
	return (CEditorDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CEditorView message handlers

BOOL CEditorView::OnEraseBkgnd(CDC* pDC) 
{
	return 1;
}

/////////////////////////////////////////////////////////////////////////////
// CEditorView drawing

void CEditorView::OnDraw(CDC* pDC)
{
	CSize tSize = GetTotalSize();

	RECT rect;
	GetClientRect(&rect);
	rect.left = 0;
	rect.top = 0;
	if(rect.right < tSize.cx)
		rect.right = tSize.cx;
	if(rect.bottom < tSize.cy)
		rect.bottom = tSize.cy;


	CMemDC* pMemDC = new CMemDC(pDC, rect);

	DoDraw(pMemDC);

	delete pMemDC;
}

void CEditorView::DoDraw(CDC* pDC)
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if(m_brBkgnd.m_hObject)
	{
		m_brBkgnd.UnrealizeObject();

		CSize tSize = GetTotalSize();
		
		RECT rect;
		GetClientRect(&rect);
		rect.left = 0;
		rect.top = 0;
		if(rect.right < tSize.cx)
			rect.right = tSize.cx;
		if(rect.bottom < tSize.cy)
			rect.bottom = tSize.cy;

		CBrush* pOld = pDC->SelectObject(&m_brBkgnd);
		pDC->FillRect(&rect, &m_brBkgnd);
		pDC->SelectObject(pOld);
	}

	POINT ptOrg;
	ptOrg.x = VIEW_ORG_X;
	ptOrg.y = VIEW_ORG_Y;

	if(pDoc->m_pGrids)
	{
		int cx = pDoc->m_nCX;
		int cy = pDoc->m_nCY;
		int tx = pDoc->m_nTx;
		int ty = pDoc->m_nTy;

		int x = ptOrg.x;
		int y = ptOrg.y;

		int nIndex = 0;

		for(int j=0; j<cy; j++)
		{
			for(int i=0; i<cx; i++)
			{
				GRID* pGrid = pDoc->m_pGrids + nIndex;
				nIndex++;

				for(int t=0; t<CTileMapKit::MAX_LEVEL; t++)
				{
					TILE* pTile = pGrid->ppTiles[t];
					if(pTile != NULL)
					{
						pTile->xImage.Draw(pDC->GetSafeHdc(), x, y);
					}
				}

				x += tx;
			}
			y += ty;
			x = ptOrg.x;
		}
	}
	if(m_bViewGrid)
	{
		int cx = pDoc->m_nCX;
		int cy = pDoc->m_nCY;
		int tx = pDoc->m_nTx;
		int ty = pDoc->m_nTy;

		int x = ptOrg.x;
		int y = ptOrg.y;

		int rx = x + cx * tx;
		int ry = y + cy * ty;

		m_pnLine.UnrealizeObject();
		CPen* pOld = pDC->SelectObject(&m_pnLine);

		for(; x<=rx; x+=tx)
		{
			pDC->MoveTo(x, y);
			pDC->LineTo(x, ry);
		}
		x = ptOrg.x;
		for(; y<=ry; y+=ty)
		{
			pDC->MoveTo(x, y);
			pDC->LineTo(rx, y);
		}
		pDC->SelectObject(pOld);
	}

	if(m_bScreen)
	{
		int x = m_ptScreen.x;
		int y = m_ptScreen.y;

		int rx = x + pDoc->m_nSx;
		int ry = y + pDoc->m_nSy;

		m_pnScreen.UnrealizeObject();
		CPen* pOld = pDC->SelectObject(&m_pnScreen);

		pDC->MoveTo(x, y);
		pDC->LineTo(x, ry);
		pDC->LineTo(rx, ry);
		pDC->LineTo(rx, y);
		pDC->LineTo(x, y);

		pDC->SelectObject(pOld);
	}

	CTilesetKit* pKit = pDoc->m_pTilesetKit;
	if(pKit)
	{
		if(pKit->m_nSelect != -1 && pDoc->m_nIndex != -1)
		{
			CRect rect;
			GetFocusRect(pDoc->m_nIndex, rect);
			int x1 = rect.left;
			int y1 = rect.top;
			int x2 = rect.right;
			int y2 = rect.bottom;

			CPen pen(0, 1, RGB(255, 0, 0));
			CPen* pOld = (CPen*)pDC->SelectObject(&pen);

			pDC->MoveTo(x1, y1);
			pDC->LineTo(x2, y1);
			pDC->LineTo(x2, y2);
			pDC->LineTo(x1, y2);
			pDC->LineTo(x1, y1);

			pDC->SelectObject(pOld);
		}
	}

}

int CEditorView::HitTest(const CPoint& pt)
{
	CPoint ptHit = GetScrollPosition();
	ptHit.x += pt.x;
	ptHit.y += pt.y;

	if(ptHit.x >= VIEW_ORG_X && ptHit.y >= VIEW_ORG_Y)
	{
		ptHit.x -= VIEW_ORG_X;
		ptHit.y -= VIEW_ORG_Y;

		CEditorDoc* pDoc = GetDocument();
		ASSERT_VALID(pDoc);

		int tx = pDoc->m_nTx;
		int ty = pDoc->m_nTy;

		int ix = ptHit.x / tx;
		int iy = ptHit.y / ty;

		if( ix >= 0 && ix < pDoc->m_nCX &&
			iy >= 0 && iy < pDoc->m_nCY )
		{
			return (iy * pDoc->m_nCX) + ix;
		}
	}
	return -1;
}

void CEditorView::PaintAt(const CPoint& pt)
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	CTilesetKit* pKit = pDoc->m_pTilesetKit;
	if(pKit != NULL && pDoc->m_nIndex != -1)
	{
		int nIndex = HitTest(pt);
		if(nIndex >= 0)
		{
			pDoc->Paint(nIndex, m_nLevel);

			CRect rect;
			if(GetFocusRect(nIndex, rect))
			{
				CPoint pts = GetScrollPosition();
				rect.OffsetRect(-pts.x, -pts.y);
				InvalidateRect(&rect);
			}
		}
	}
}

void CEditorView::EraseAt(const CPoint& pt)
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	int nIndex = HitTest(pt);
	if(nIndex >= 0)
	{
		pDoc->Erase(nIndex, m_nLevel);

		CPoint sp = GetScrollPosition();

		int ix = nIndex % (pDoc->m_nCX);
		int iy = nIndex / (pDoc->m_nCX);

		RECT rect;
		rect.left = ix * pDoc->m_nTx + VIEW_ORG_X - sp.x;
		rect.top = iy * pDoc->m_nTy + VIEW_ORG_Y - sp.y;
		rect.right = rect.left + pDoc->m_nTx;
		rect.bottom = rect.top + pDoc->m_nTy;

		InvalidateRect(&rect);
	}
}

void CEditorView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	if(m_bErase)
	{
		EraseAt(point);
	}
	else
	{
		PaintAt(point);
	}
	CScrollView::OnLButtonDown(nFlags, point);
}

bool CEditorView::GetFocusRect(int nIndex, CRect& rect)
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	
	CTilesetKit* pKit = pDoc->m_pTilesetKit;
	if(pKit)
	{
		POINT ptOrg;
		ptOrg.x = VIEW_ORG_X;
		ptOrg.y = VIEW_ORG_Y;
		
		rect.left = (nIndex % pDoc->m_nCX) * pKit->m_nTx + ptOrg.x;
		rect.top = (nIndex / pDoc->m_nCX) * pKit->m_nTy + ptOrg.y;
		rect.right = rect.left + (pKit->m_nCX * pKit->m_nTx);
		rect.bottom = rect.top + (pKit->m_nCY * pKit->m_nTy);

		return true;
	}
	return false;
}

void CEditorView::OnMouseMove(UINT nFlags, CPoint point) 
{
	int nIndex = HitTest(point);
	if(nIndex != -1)
	{
		CEditorDoc* pDoc = GetDocument();
		ASSERT_VALID(pDoc);

		CTilesetKit* pKit = pDoc->m_pTilesetKit;
		if(pKit)
		{
			pDoc->m_nIndex = nIndex;

			CRect rect;
			GetFocusRect(nIndex, rect);
			rect.right++;
			rect.bottom++;

			CPoint pts = GetScrollPosition();

			if(!m_rBox.IsRectEmpty())
			{
				m_rBox.OffsetRect(-pts.x, -pts.y);
				InvalidateRect(&m_rBox);
			}
			m_rBox = rect;
			rect.OffsetRect(-pts.x, -pts.y);
			InvalidateRect(&rect);
		}
	}

	CScrollView::OnMouseMove(nFlags, point);
}

BOOL CEditorView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 
{
	CPoint pts = GetScrollPosition();
	pts.y -= zDelta;
	ScrollToPosition(pts);
	
	return CScrollView::OnMouseWheel(nFlags, zDelta, pt);
}

void CEditorView::OnUpdateViewGrid(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_bViewGrid);
	
}

void CEditorView::OnViewGrid() 
{
	m_bViewGrid = !m_bViewGrid;
	Invalidate();
}

void CEditorView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	RECT rect;

	rect.left = m_ptScreen.x;
	rect.top = m_ptScreen.y;
	rect.right = rect.left + pDoc->m_nSx + 2;
	rect.bottom = rect.top + pDoc->m_nSy + 2;

	BOOL bChange = FALSE;

	switch(nChar)
	{
	case VK_LEFT:
		m_ptScreen.x -= nRepCnt * (pDoc->m_nTx);
		if(m_ptScreen.x < VIEW_ORG_X)
		{
			m_ptScreen.x = VIEW_ORG_X;
		}
		bChange = TRUE;
		break;
	case VK_RIGHT:
		m_ptScreen.x += nRepCnt * (pDoc->m_nTx);
		bChange = TRUE;
		break;
	case VK_UP:
		m_ptScreen.y -= nRepCnt * (pDoc->m_nTy);
		if(m_ptScreen.y < VIEW_ORG_Y)
		{
			m_ptScreen.y = VIEW_ORG_Y;
		}
		bChange = TRUE;
		break;
	case VK_DOWN:
		m_ptScreen.y += nRepCnt * (pDoc->m_nTy);
		bChange = TRUE;
		break;
	}
	if(bChange)
	{
		InvalidateRect(&rect);
		rect.left = m_ptScreen.x;
		rect.top = m_ptScreen.y;
		rect.right = rect.left + pDoc->m_nSx + 2;
		rect.bottom = rect.top + pDoc->m_nSy + 2;
		InvalidateRect(&rect);
	}
	
	CScrollView::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CEditorView::OnEditUndo() 
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if(pDoc->Undo(false))
	{
		InvalidateRect(NULL);
	}
}


void CEditorView::OnEditRedo() 
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if(pDoc->Redo(false))
	{
		InvalidateRect(NULL);
	}
}


void CEditorView::OnUpdateEditUndo(CCmdUI* pCmdUI) 
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if(pDoc->Undo(true))
	{
		pCmdUI->Enable();
	}
	else
	{
		pCmdUI->Enable(FALSE);
	}
}

void CEditorView::OnUpdateEditRedo(CCmdUI* pCmdUI) 
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if(pDoc->Redo(true))
	{
		pCmdUI->Enable();
	}
	else
	{
		pCmdUI->Enable(FALSE);
	}
}

void CEditorView::OnUpdateErase(CCmdUI* pCmdUI) 
{
	pCmdUI->SetCheck(m_bErase);
}

void CEditorView::OnErase() 
{
	m_bErase = !m_bErase;
}

void CEditorView::OnClearAll() 
{
	CEditorDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	pDoc->Clear();

	Invalidate();	
}

void CEditorView::SetLevel(int nLevel)
{
	m_nLevel = nLevel;
}

int CEditorView::GetLevel()const
{
	return m_nLevel;
}